#-*- coding: utf-8 -*-

import os
import datetime
import sys
import json
import types
import functools
import ConfigParser
import logging

import sqlalchemy.interfaces
from sqlalchemy import engine
from sqlalchemy import event
from sqlalchemy.exc import DisconnectionError, OperationalError, IntegrityError, ResourceClosedError 
from sqlalchemy.pool import NullPool
from sqlalchemy.orm import scoped_session, sessionmaker

import Ump
from Ump.objs.db.listen import my_connect, my_checkout
from Ump import utils


LOG = logging.getLogger('Ump.objs.db.utils')


_ENGINE = None
_MAKER = None


def utcnow():
    
    """Overridable version of utils.utcnow."""
    if utcnow.override_time:
        return utcnow.override_time
    return datetime.datetime.utcnow()

utcnow.override_time = None


def todaynow():
    if todaynow.override_time:
        return utcnow.override_time
    return datetime.datetime.today()

todaynow.override_time = None


def yesterdaynow():
    if todaynow.override_time:
        return utcnow.override_time - datetime.timedelta(days=1)
    return datetime.datetime.today() - datetime.timedelta(days=1)


def timedelta(**kw):
    return datetime.timedelta(kw)


def raise_duplicate_err(cls, e):
    if 'Cannot end transaction context' in str(e) \
           or 'This transaction is closed' in str(e)\
               or 'Duplicate entry' in str(e):
        raise Exception('创建失败，已存在相同记录')
    else:
        raise Exception(e)


def duplicate_err2zh(func):
    @functools.wraps(func)
    def new_func(*args,**kwargs):
        try:
            return func(*args, **kwargs)
        except ResourceClosedError, e:
            raise_duplicate_err(IntegrityError, e)
        except IntegrityError, e:
            raise_duplicate_err(IntegrityError, e)
    return new_func


def get_db_config():
    config=ConfigParser.RawConfigParser()
    # db_cfg_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)),'..','..','etc'))
    db_cfg_path = '%s/etc/ump' % utils.install_path
    config.read(os.path.join(db_cfg_path,'db.cfg'))

    if config.has_section('sqlite'):
        sql_connection = config.get('sqlite', 'name')
        return sql_connection

    if config.has_section('mysql'):
        user = config.get('mysql', 'user')
        password = config.get('mysql', 'password')
        database = config.get('mysql', 'database')
        host = config.get('mysql', 'host')
        port = config.get('mysql', 'port')
        sql_connection = 'mysql://%s:%s@%s:%s/%s?charset=utf8'%(user,password,host,port,database)
        return sql_connection


def get_session(autocommit=True, expire_on_commit=False):
    """Return a SQLAlchemy session."""
    global _MAKER

    if _MAKER is None:
        engine = get_engine()
        if engine is None:
            return None
        _MAKER = get_maker(engine, autocommit, expire_on_commit)

    return _MAKER()
    # return res


def get_engine():
    """Return a SQLAlchemy engine."""
    global _ENGINE
    if _ENGINE is None:
        sql_connection = get_db_config()
        if sql_connection is None:
            return None
        connection_dict = engine.url.make_url(sql_connection)

        engine_args = {
            'echo': False,
            'convert_unicode': True,
            'encoding':'utf8',
            'pool_recycle': 7200,
        }

        #sqlit在使用文件做为数据库时，默认的选项是NullPool
        if "sqlite" in connection_dict.drivername:
            engine_args["poolclass"] = NullPool

        _ENGINE = sqlalchemy.create_engine(sql_connection, **engine_args)

        if "sqlite" in connection_dict.drivername:
            event.listen(_ENGINE, 'connect', my_connect)

        if 'mysql' in connection_dict.drivername:
            event.listen(_ENGINE, 'checkout', my_checkout)

        try:
            _ENGINE.connect()
        except OperationalError, e:
            raise e

    return _ENGINE

def get_maker(engine, autocommit=True, expire_on_commit=False):
    """Return a SQLAlchemy sessionmaker using the given engine."""
    Session = sessionmaker(bind=engine,
                           autocommit=autocommit,
                           expire_on_commit=expire_on_commit)
    #在session出现异常时进行回滚
    #class Uss_session(Session):
    #    def commit(self):
    #        try:
    #            super(Session, self).commit()
    #        except Exception,e:
    #            super(Session, self).rollback()
    #            reraise(sys.exc_info())
    return scoped_session(Session)


def get_session2(autocommit=True, expire_on_commit=False):
    engine = get_engine()
    session = sessionmaker(bind=engine,
                           autocommit=autocommit,
                           expire_on_commit=expire_on_commit)

    return session()

def reraise(exc_info=(None, None, None), errorMsg=''):
    """Re-raise the latest exception given by exc_info tuple (as returned by
    sys.exc_info()) with additional errorMsg text.
    Exceptions with non-standard constructors get re-raised as derived
    exceptions, with recorded original error message and original traceback.
    Parameters:
        exc_info: (<exception class>, <exception instance>, <traceback object>)
            tuple
        errorMsg: error message text to add to the exception error message

        ...     try:
        ...             b = str(s)
        ...     except:
        ...             reraise(sys.exc_info(), "really common problem")
        ...
    """
    excClass, exc, tb = exc_info
    try:
        # re-raise original exception with added custom error message
        raise excClass, excClass("%s: %s" % (exc, errorMsg)), tb
    except TypeError:
        if excClass == TypeError:
            # original exception is TypeError, which has a standard constructor:
            # safe to re-raise this way
            raise excClass, excClass("%s: %s" % (exc, errorMsg)), tb

        # TypeError due to non-standard exception constructor
        if isinstance(excClass, (types.ClassType, type)) \
           and issubclass(excClass, Exception):
            # raise derived exception class with added custom error message
            class CustomInfoException(excClass):
                def __init__(self, info=''):
                    self._info = info

                def __str__(self):
                    return "%s" % (self._info)

            CustomInfoException.__name__ = excClass.__name__
            raise CustomInfoException, \
                  CustomInfoException(info='%s, %s'%(exc.args, errorMsg)), tb
        else:
            # raise base Exception class with added original exception
            # message plus custom error message. Safe for old string exceptions.
            raise Exception, \
                  Exception("%s: %s: %s"
                            % (getattr(excClass, '__name__', excClass), exc or
                               excClass, errorMsg)), tb


def date2json(obj):
    result = json.dumps(obj, cls=DateEncoder)
    return result

#if __name__ == '__main__':
#    x  = get_engine()
